/*
 * ape.c
 *
 *  Created on: 20/07/2012
 *      Author: annibal
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "ape.h"
#include "leitura.h"

void criaAPE(FILE *espec, APE *M) {
	int i;
	char *nomeSubmaquinas;

	M->numeroSubmaquinas = leInteiro(espec);
	M->submaquinas = (Submaquina*) malloc(
			M->numeroSubmaquinas * sizeof(Submaquina));

	for (i = 0; i < M->numeroSubmaquinas; i++) {
		M->submaquinas[i].nome = leCaractere(espec);
	}
	M->submaquinaInicial = M->submaquinas;

	geraAlfabetoAPE(espec, M);

	M->pilha = novaPilha();

	nomeSubmaquinas = (char*) malloc(M->numeroSubmaquinas * sizeof(char));

	for (i = 0; i < M->numeroSubmaquinas; i++) {
		nomeSubmaquinas[i] = M->submaquinas[i].nome;
	}

	for (i = 0; i < M->numeroSubmaquinas; i++) {
		criaSubmaquina(M->tamAlfabeto, M->alfabeto, M->numeroSubmaquinas,
				nomeSubmaquinas, espec, M->submaquinas + i);
		M->submaquinas[i].AF.leitor = &(M->token);
	}

	M->submaquinaAtual = M->submaquinaInicial;

	free(nomeSubmaquinas);
}

void geraAlfabetoAPE(FILE *espec, APE *M) {
	int i;
	char token;

	/* Lê o tamanho do alfabeto */
	M->tamAlfabeto = leInteiro(espec);
	M->alfabeto = (char*) malloc(M->tamAlfabeto * sizeof(char));

	for (i = 0; i < M->tamAlfabeto; i++) {
		token = leCaractere(espec);
		M->alfabeto[i] = token;
	}
}

void criaSubmaquina(int tamAlfabeto, char *alfabeto, int numeroSubmaquinas,
		char *nomeSubmaquinas, FILE *espec, Submaquina *a) {
	Automato *AF;

	AF = &(a->AF);

	criaEstados(AF, espec);
	AF->estadoInicial = AF->estados;
	AF->estadoAtual = AF->estadoInicial;
	AF->tamAlfabeto = tamAlfabeto;
	AF->alfabeto = alfabeto;

	criaTransicoesSM(numeroSubmaquinas, nomeSubmaquinas, espec, a);
}

void criaTransicoesSM(int numeroSubmaquinas, char *nomeSubmaquinas, FILE *espec,
		Submaquina *a) {
	int i, fim;
	Automato *AF;

	AF = &(a->AF);

	AF->transicoes = (Estado***) malloc(AF->numEstados * sizeof(Estado**));
	for (i = 0; i < AF->numEstados; i++) {
		AF->transicoes[i] = (Estado**) calloc(
				AF->tamAlfabeto + numeroSubmaquinas, sizeof(Estado*));
	}

	while (fim == FALSE) {
		fim = adicionaTransicaoSM(numeroSubmaquinas, nomeSubmaquinas, espec, a);
	}
}

int adicionaTransicaoSM(int numeroSubmaquinas, char *nomeSubmaquinas,
		FILE *espec, Submaquina *a) {
	int i, j, k, achou = FALSE, fim = FALSE;
	char *nomeAtual = NULL, *nomeProximo = NULL, rotulo;
	Estado *proximo = NULL;
	Automato *AF;

	AF = &(a->AF);

	nomeAtual = leString(espec);

	if (nomeAtual != NULL) {
		for (i = 0; i < AF->numEstados && achou == FALSE; i++) {
			if (strcmp(AF->estados[i].nome, nomeAtual) == 0) {
				achou = TRUE;
				free(nomeAtual);
			}
		}
	} else {
		fim = TRUE;
	}

	if (fim == FALSE) {
		rotulo = leCaractere(espec);
		nomeProximo = leString(espec);

		/* Verifica se o estado de destino da transição é válido */
		for (k = 0; k < AF->numEstados && proximo == NULL; k++) {
			if (strcmp(AF->estados[k].nome, nomeProximo) == 0) {
				proximo = AF->estados + k;
				if (nomeProximo != NULL) {
					free(nomeProximo);
				}
			}
		}

		/* Verifica se o rótulo é válido */
		achou = FALSE;
		for (j = 0; j < AF->tamAlfabeto && achou == FALSE; j++) {
			if (AF->alfabeto[j] == rotulo) {
				achou = TRUE;
			}
		}

		if (achou == FALSE) {
			for (; j < AF->tamAlfabeto + numeroSubmaquinas && achou == FALSE;
					j++) {
				if (nomeSubmaquinas[j - AF->tamAlfabeto] == rotulo) {
					achou = TRUE;
				}
			}
		}

		if (achou == TRUE) {
			AF->transicoes[i - 1][j - 1] = proximo;
		}
	}

	return fim;
}

void recebeTokenAPE(char token, APE *M) {
	M->token = token;
}

char analisaToken(APE *M, char imprime, FILE *saida) {
	int i, j, achou = FALSE, prossegue, retornaToken;
	Automato *AF;
	Estado *estado = NULL;
	char token;
	Elemento *e;

	AF = &(M->submaquinaAtual->AF);

	if (imprime == 's' || imprime == 'S') {
		if (M->token != 0) {
			fprintf(saida, "%10c", M->token);
		} else {
			fprintf(saida, "%10s", "(null)");
		}
	}

	if (M->token != 0) {
		for (i = 0; i < AF->numEstados && achou == FALSE; i++) {
			if (AF->estados + i == AF->estadoAtual) {
				achou = TRUE;
			}
		}

		achou = FALSE;
		for (j = 0; j < AF->tamAlfabeto && achou == FALSE; j++) {
			if (AF->alfabeto[j] == *(AF->leitor)) {
				estado = M->submaquinaAtual->AF.transicoes[i - 1][j];
				achou = TRUE;
			}
		}

		if (achou == FALSE || estado == NULL) {
			for (j = M->tamAlfabeto;
					j < M->tamAlfabeto + M->numeroSubmaquinas && estado == NULL;
					j++) {
				if (M->submaquinaAtual->AF.transicoes[i - 1][j] != NULL) {
					estado = M->submaquinaAtual->AF.transicoes[i - 1][j];
				}
			}
		}
	}

	if (estado != NULL) {
		prossegue = TRUE;
		if (j <= M->tamAlfabeto) {
			AF->estadoAtual = estado;
			retornaToken = FALSE;

			if (imprime == 's' || imprime == 'S') {
				fprintf(saida, "%10s", "");
			}
		} else {
			empilha(estado, &(M->pilha));
			M->submaquinaAtual = M->submaquinas + (j - M->tamAlfabeto - 1);
			M->submaquinaAtual->AF.estadoAtual =
					M->submaquinaAtual->AF.estadoInicial;

			if (imprime == 's' || imprime == 'S') {
				fprintf(saida, "%10c", M->submaquinaAtual->nome);
			}

			retornaToken = TRUE;
		}
	} else if (AF->estadoAtual->final == TRUE && !pilhaVazia(M->pilha)) {
		estado = desempilha(&(M->pilha));
		M->submaquinaAtual = encontraSubmaquina(estado, M);
		M->submaquinaAtual->AF.estadoAtual = estado;
		retornaToken = TRUE;
		prossegue = TRUE;

		if (imprime == 's' || imprime == 'S') {
			fprintf(saida, "%10s", "");
		}
	} else {
		prossegue = FALSE;

		if (imprime == 's' || imprime == 'S') {
			fprintf(saida, "%10s", "");
		}
	}

	if (prossegue == TRUE) {
		if (retornaToken == TRUE) {
			token = M->token;

			if (imprime == 's' || imprime == 'S') {
				if (token == 0) {
					fprintf(saida, "%10s", "(null)");
				} else {
					fprintf(saida, "%10c", token);
				}
			}
		} else {
			token = 0;

			if (imprime == 's' || imprime == 'S') {
				fprintf(saida, "%10s", "");
			}
		}
	} else {
		if (imprime == 's' || imprime == 'S') {
			fprintf(saida, "%10s", "");
		}

		token = -1;
	}

	if (imprime == 's' || imprime == 'S') {
		if (pilhaVazia(M->pilha)) {
			fprintf(saida, "%10s", "vazia");
		} else {
			e = M->pilha->topo;
			fprintf(saida, "\t%4c%s", encontraSubmaquina(e->estado, M)->nome,
					e->estado->nome);
			e = e->prox;

			while (e != NULL) {
				fprintf(saida, ",%c%s", encontraSubmaquina(e->estado, M)->nome,
						e->estado->nome);
				e = e->prox;
			}
		}
	}

	return token;
}

Submaquina *encontraSubmaquina(Estado *estado, APE *M) {
	int i, j, achou = FALSE;

	for (i = 0; achou == FALSE && i < M->numeroSubmaquinas; i++) {
		for (j = 0; achou == FALSE && j < M->submaquinas[i].AF.numEstados;
				j++) {
			if (estado == M->submaquinas[i].AF.estados + j) {
				achou = TRUE;
			}
		}
	}

	return M->submaquinas + (i - 1);
}

int aceita(APE *M) {
	if (M->submaquinaAtual == M->submaquinaInicial
			&& M->submaquinaAtual->AF.estadoAtual->final == TRUE
			&& pilhaVazia(M->pilha) && M->token == 0) {
		return TRUE;
	} else {
		return FALSE;
	}
}

void inicializaAPE(APE *M) {
	M->submaquinaAtual = M->submaquinaInicial;
	M->submaquinaAtual->AF.estadoAtual = M->submaquinaInicial->AF.estadoInicial;
	M->token = 0;

	while (!pilhaVazia(M->pilha)) {
		desempilha(&(M->pilha));
	}
}
